/* tz_arraylist.c.t
 *
 * 15-Feb-2008 Initial write: Ting Zhao
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tz_error.h"
#include "tz_utilities.h"
#include "tz_stack_tile_i_arraylist.h"

INIT_EXCEPTION

const static int Growth_Coeff = 2; /* growth factor */

/* stack_tile_i_arraylist_grow(): Grow an arraylist.
 *
 * Args: a - the arraylist to grow.
 *
 * Return: void.
 */
static void stack_tile_i_arraylist_grow(Stack_Tile_I_Arraylist *a)
{
  if (a->capacity == 0) {
    a->capacity = Growth_Coeff;
    a->array = (Stack_Tile_I *) Guarded_Malloc(sizeof(Stack_Tile_I) * a->capacity, 
				       "stack_tile_i_arraylist_grow");
  } else {
    int new_capacity = Growth_Coeff * a->capacity;
  
    Stack_Tile_I *new_array = (Stack_Tile_I *) Guarded_Calloc(new_capacity, sizeof(Stack_Tile_I),
					      "stack_tile_i_arraylist_grow");
    memcpy(new_array, a->array, sizeof(Stack_Tile_I) * a->capacity);
    free(a->array);
    a->array = new_array;
    a->capacity = new_capacity;
  }
}

/* Print_Stack_Tile_I_Arraylist(): Prints an array list.
 * 
 * Args: a - the arraylist to print.
 *       format - printing format. This is mainly for priliminary data elements.
 *
 * Return: void.
 */
void Print_Stack_Tile_I_Arraylist(const Stack_Tile_I_Arraylist *a)
{
  int i;
  printf("%d elements: ", a->length);
  for  (i = 0; i < a->length; i++) {
    Print_Stack_Tile_I(a->array + i);
  }
  
  printf("\n");
}

Stack_Tile_I_Arraylist* New_Stack_Tile_I_Arraylist()
{
  return Stack_Tile_I_Arraylist_New(0, 1);
}

/* Stack_Tile_I_Arraylist_New(): New an arraylist.
 * 
 * Args: length - length of the arraylist;
 *       margin - margin of the arraylist. This means the capacity of the
 *                arraylist will be <length> + <margin>.
 * 
 * Return: a new arraylist.
 */
Stack_Tile_I_Arraylist* Stack_Tile_I_Arraylist_New(int length, int margin)
{
  if ((length < 0) || margin < 0) {
    TZ_WARN(ERROR_DATA_VALUE);
    return NULL;
  }

  Stack_Tile_I_Arraylist *a = (Stack_Tile_I_Arraylist *) Guarded_Malloc(sizeof(Stack_Tile_I_Arraylist), 
							"Stack_Tile_I_Arraylist_New");
  a->length = length;
  a->capacity = length + margin;
  if (a->capacity > 0) {
    a->array = (Stack_Tile_I *) Guarded_Malloc(sizeof(Stack_Tile_I) * a->capacity, 
				       "Stack_Tile_I_Arraylist_New");
  } else {
    a->array = NULL;
  }

  return a;
}

/* Stack_Tile_I_Arraylist_Free(): Free an array list.
 *
 * Args: a - the array list to free.
 *
 * Return: void.
 */
void Stack_Tile_I_Arraylist_Free(Stack_Tile_I_Arraylist *a)
{
  free(a->array);
  free(a);
}

Stack_Tile_I_Arraylist* Make_Stack_Tile_I_Arraylist(int length, int margin)
{
  if ((length < 0) || margin < 0) {
    TZ_WARN(ERROR_DATA_VALUE);
    return NULL;
  }

  Stack_Tile_I_Arraylist *a = (Stack_Tile_I_Arraylist *) Guarded_Malloc(sizeof(Stack_Tile_I_Arraylist), 
							"Stack_Tile_I_Arraylist_New");
  a->length = length;
  a->capacity = length + margin;
  if (a->capacity > 0) {
    a->array = (Stack_Tile_I *) Guarded_Malloc(sizeof(Stack_Tile_I) * a->capacity, 
				       "Stack_Tile_I_Arraylist_New");
  } else {
    a->array = NULL;
  }

  return a;
}

void Kill_Stack_Tile_I_Arraylist(Stack_Tile_I_Arraylist *a)
{
  
  int i;
  for (i = 0; i < a->length; i++) {
    Clean_Stack_Tile_I(a->array + i);
  }
  
  
  free(a->array);
  free(a);
}

Stack_Tile_I_Arraylist* Stack_Tile_I_Arraylist_Wrap(Stack_Tile_I *array, int n)
{
  if (array == NULL) {
    return NULL;
  }

  Stack_Tile_I_Arraylist *a = (Stack_Tile_I_Arraylist *) Guarded_Malloc(sizeof(Stack_Tile_I_Arraylist), 
							"Stack_Tile_I_Arraylist_New");
  a->length = n;
  a->capacity = n;
  a->array = array;

  return a;
}

Stack_Tile_I Stack_Tile_I_Arraylist_At(const Stack_Tile_I_Arraylist *a, int index)
{
  return a->array[index];
}

Stack_Tile_I Stack_Tile_I_Arraylist_Last(const Stack_Tile_I_Arraylist *a)
{
  return a->array[a->length - 1];
}

Stack_Tile_I* Stack_Tile_I_Arraylist_At_R(const Stack_Tile_I_Arraylist *a, int index)
{
  return a->array + index;
}

/* Stack_Tile_I_Arraylist_Add(): Add an element to an array list.
 *
 * Args: <a> - the arraylist to manipulate;
 *       <item> - the element to add.
 *
 * Return: void.
 */
void Stack_Tile_I_Arraylist_Add(Stack_Tile_I_Arraylist *a, Stack_Tile_I item)
{
  a->length++;

  if (a->length >= a->capacity) {
    stack_tile_i_arraylist_grow(a);
  }

  a->array[a->length - 1] = item;
}

void Stack_Tile_I_Arraylist_Insert(Stack_Tile_I_Arraylist *a, int index, Stack_Tile_I item)
{
  if (index < a->length) {
    a->length++;
    if (a->length >= a->capacity) {
      stack_tile_i_arraylist_grow(a);
    }

    memmove(a->array + index + 1, a->array + index, 
	    sizeof(Stack_Tile_I) * (a->length - index - 1));
  }

  Stack_Tile_I_Arraylist_Set(a, index, item);
}

Stack_Tile_I Stack_Tile_I_Arraylist_Take(Stack_Tile_I_Arraylist *a, int index)
{
  Stack_Tile_I data = a->array[index];
  a->length--;
  memmove(a->array + index, a->array + index + 1, 
	  sizeof(Stack_Tile_I) * (a->length - index));

  return data;
}

Stack_Tile_I Stack_Tile_I_Arraylist_Take_Last(Stack_Tile_I_Arraylist *a)
{
  Stack_Tile_I data = a->array[a->length - 1];
  a->length--;

  return data;
}

void Stack_Tile_I_Arraylist_Set_Length(Stack_Tile_I_Arraylist *a, int length)
{
  TZ_ASSERT(length >= 0, "Negative length");

  a->length = length;

  while (a->length > a->capacity) {
    stack_tile_i_arraylist_grow(a);
  }
}

void Stack_Tile_I_Arraylist_Set(Stack_Tile_I_Arraylist *a, int index, Stack_Tile_I item)
{
  if (index >= a->length) {
    a->length = index + 1;
  }

  while (a->length > a->capacity) {
    stack_tile_i_arraylist_grow(a);
  }
  

  a->array[index] = item;
}

/* Stack_Tile_I_Arraylist_Swap(): Swap two elements
 * 
 * Args: a - the arraylist to manipulate;
 *       idx1 - index of the first element;
 *       idx2 - index of the second element.
 *
 * Return: void.
 */
void Stack_Tile_I_Arraylist_Swap(Stack_Tile_I_Arraylist *a, int idx1, int idx2)
{
  if ((idx1 < 0) || (idx1 >= a->length) || (idx2 < 0) || (idx2 >= a->length)) {
    TZ_WARN(ERROR_DATA_VALUE);
    return;
  }
  
  Stack_Tile_I tmp;
  tmp = a->array[idx1];
  a->array[idx1] = a->array[idx2];
  a->array[idx2] = tmp;
}

